package com.coderforum.community.service;

import com.coderforum.community.dto.PaginationDTO;
import com.coderforum.community.dto.QuestionDTO;
import com.coderforum.community.dto.QuestionQueryDTO;
import com.coderforum.community.exception.CustomizeErrorCode;
import com.coderforum.community.exception.CustomizeException;
import com.coderforum.community.mapper.QuestionExtMapper;
import com.coderforum.community.mapper.QuestionMapper;
import com.coderforum.community.mapper.UserMapper;
import com.coderforum.community.model.Question;
import com.coderforum.community.model.QuestionExample;
import com.coderforum.community.model.User;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 可以使用UserMapper和QuestionMapper，起到一个组装的作用
 * 请求需要组装User和Question时，需要这个中间层做这件事情
 *
 * @Author: xuehai.XUE
 * @MailBox: xuehai.xue@qq.com
 * @Date: 2021/4/13 17:17
 */
@Service
public class QuestionService {
    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private QuestionExtMapper questionExtMapper;

    /**
     * 首页展示列表，根据指定的page页码和size步长分页
     *
     * @param page 页码
     * @param size 分页步长
     * @return 返回paginationDTO的list对象
     */
    public PaginationDTO list(String search, Integer page, Integer size) {

        if(StringUtils.isNoneBlank(search)){
            String[] tags = StringUtils.split(search, " ");
            search = Arrays.stream(tags).collect(Collectors.joining("|"));
        }

        PaginationDTO paginationDTO = new PaginationDTO();
        //查询数据表中的数据总数
        Integer totalPage;
        //查询数据表中的数据总数
        QuestionQueryDTO questionQueryDTO = new QuestionQueryDTO();
        questionQueryDTO.setSearch(search);
        Integer totalCount = questionExtMapper.countBySearch(questionQueryDTO);

        if (totalCount % size == 0) {
            totalPage = totalCount / size;
        } else {
            totalPage = totalCount / size + 1;
        }

        paginationDTO.setPagination(totalPage, page);

        //页码范围逻辑校验
        if (page < 1) {
            page = 1;
        }
        if (page > totalPage) {
            page = totalPage;
        }
        //size*(page-1) 计算sql中select语句的limit
        Integer offset = size * (page - 1);
        questionQueryDTO.setSize(size);
        questionQueryDTO.setPage(offset);
        List<Question> questions = questionExtMapper.selectBySearch(questionQueryDTO);
        List<QuestionDTO> questionDTOList = new ArrayList<>();

        for (Question question : questions) {
            User user = userMapper.selectByPrimaryKey(question.getCreator());
            //把所有question对象放到questionDTO中
            QuestionDTO questionDTO = new QuestionDTO();
            //BeanUtils类的目的是快速的把question中的属性复制到questionDTO中（底层通过反射的方式获取属性再复制）
            BeanUtils.copyProperties(question, questionDTO);
            questionDTO.setUser(user);
            //每次创建完新的questionDTO后add进questionDTOList中
            questionDTOList.add(questionDTO);
        }
        paginationDTO.setData(questionDTOList);

        return paginationDTO;
    }

    /**
     * 用户个人页面的问题列表展示，根据传入的UserId和page以及size进行问题的分页展示
     *
     * @param userId 指定的用户id
     * @param page   页码
     * @param size   分页步长
     * @return 返回paginationDTO的list对象
     */
    public PaginationDTO list(Long userId, Integer page, Integer size) {
        PaginationDTO paginationDTO = new PaginationDTO();
        Integer totalPage;
        //查询数据表中的数据总数
        QuestionExample questionExample = new QuestionExample();
        Integer totalCount = (int) questionMapper.countByExample(questionExample);
        questionExample.createCriteria()
                .andCreatorEqualTo(userId);

        if (totalCount % size == 0) {
            totalPage = totalCount / size;
        } else {
            totalPage = totalCount / size + 1;
        }

        //页码范围逻辑校验
        if (page < 1) {
            page = 1;
        }
        if (page > totalPage) {
            page = totalPage;
        }

        paginationDTO.setPagination(totalPage, page);

        //size*(page-1) 计算sql中select语句的limit
        Integer offset = size * (page - 1);

        QuestionExample example = new QuestionExample();
        example.createCriteria()
                .andCreatorEqualTo(userId);
        //使用mybatis generator官方的插件RowBoundsPlugin，写在了generatorConfig.xml中
        List<Question> questions = questionMapper.selectByExampleWithRowbounds(example, new RowBounds(offset, size));
        List<QuestionDTO> questionDTOList = new ArrayList<>();

        for (Question question : questions) {
            User user = userMapper.selectByPrimaryKey(question.getCreator());
            //把所有question对象放到questionDTO中
            QuestionDTO questionDTO = new QuestionDTO();
            //BeanUtils类的目的是快速的把question中的属性复制到questionDTO中（底层通过反射的方式获取属性再复制）
            BeanUtils.copyProperties(question, questionDTO);
            questionDTO.setUser(user);
            //每次创建完新的questionDTO后add进questionDTOList中
            questionDTOList.add(questionDTO);
        }
        paginationDTO.setData(questionDTOList);

        return paginationDTO;
    }

    /**
     * 通过QuestionService调用QuestionMapper做数据库的交互
     *
     * @param id 通过指定的id修改相应id的问题
     * @return 返回经过组装的questionDTO对象
     */
    public QuestionDTO getById(Long id) {
        Question question = questionMapper.selectByPrimaryKey(id);
        //当前端查询一个不存在的问题，直接通过URL的方式查询，如果这问题不存在，则返回问题查询不到的信息
        if (question == null) {
            throw new CustomizeException(CustomizeErrorCode.QUESTION_NOT_FOUND);
        }
        QuestionDTO questionDTO = new QuestionDTO();
        BeanUtils.copyProperties(question, questionDTO);
        User user = userMapper.selectByPrimaryKey(question.getCreator());
        questionDTO.setUser(user);
        return questionDTO;
    }

    /**
     * 创建或者更新问题
     * @param question 传入可能需要修改的问题 或者直接创建新问题
     */
    public void createOrUpdate(Question question) {
        if (question.getId() == null) {
            //问题的id不存在，则创建问题
            question.setGmtCreate(System.currentTimeMillis());
            question.setGmtModified(question.getGmtCreate());
            //创建问题的时候初始化问题的阅读数、评论数、点赞数
            question.setViewCount(0);
            question.setCommentCount(0);
            question.setLikeCount(0);
            questionMapper.insert(question);
        } else {
            //问题的id存在则修改
            Question updateQuestion = new Question();
            updateQuestion.setGmtModified(System.currentTimeMillis());
            updateQuestion.setTitle(question.getTitle());
            updateQuestion.setDescription(question.getDescription());
            updateQuestion.setTag(question.getTag());
            QuestionExample example = new QuestionExample();
            example.createCriteria()
                    .andIdEqualTo(question.getId());
            int updated = questionMapper.updateByExampleSelective(updateQuestion, example);
            //当用户想要更改的问题时，如果改问题已被删除，则返回错误信息
            if(updated != 1){
                throw new CustomizeException(CustomizeErrorCode.QUESTION_NOT_FOUND);
            }
        }
    }

    /**
     * 用于控制阅读数增加，使用QuestionExtMapper.xml以及
     * 使用QuestionExtMapper.java文件解决数据库更新过程中出现线程不安全的问题
     * @param id 待更新问题阅读数的id
     */
    public void incView(Long id) {
        //创建一个Question对象question
        Question question = new Question();
        //设置question的id
        question.setId(id);
        //设置当前Question对象question的阅读数为 1
        question.setViewCount(1);
        /* 使用非自动生成的Mapper增加 1，其中原理是数据库中的阅读数加上上述的quesion阅读数即 1，
           作为新值赋给数据库中的阅读数*/
        questionExtMapper.incView(question);
    }

    /**
     * 用于标签的模糊查询
     * @param queryDTO 包含有question.id的DTO对象
     * @return
     */
    public List<QuestionDTO> selectRelated(QuestionDTO queryDTO) {
        if(StringUtils.isBlank(queryDTO.getTag())){
            return new ArrayList<>();
        }
        String[] tags = StringUtils.split(queryDTO.getTag(), ",");
        String regexpTag = Arrays.stream(tags).collect(Collectors.joining("|"));
        Question question = new Question();
        question.setId(queryDTO.getId());
        question.setTag(regexpTag);

        List<Question> questions = questionExtMapper.selectRelated(question);
        List<QuestionDTO> questionDTOS = questions.stream().map(q -> {
            QuestionDTO questionDTO = new QuestionDTO();
            BeanUtils.copyProperties(q,questionDTO);
            return questionDTO;
        }).collect(Collectors.toList());
        return questionDTOS;
    }
}
